// special markup used throughout Ext when box wrapping elements
Ext.Element.boxMarkup =
'<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';

Ext.Element.addMethods(function()
{
	var INTERNAL = "_internal", pxMatch = /(\d+\.?\d+)px/;
	var pub =
	{
		// 便捷写法
		applyStyles: function( style )
		{
			Ext.DomHelper.applyStyles(this.dom, style);
			return this;
		},

		/**
		 * Returns an object with properties matching the styles requested. For example, el.getStyles('color', 'font-size', 'width') might return {'color':
		 * '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
		 * 
		 * @param {String} style1 A style name
		 * @param {String} style2 A style name
		 * @param {String} etc.
		 * @return {Object} The style object
		 */
		getStyles: function()
		{
			var ret = {};
			Ext.each(arguments, function( v )
			{
				ret[v] = this.getStyle(v);
			}, this);
			return ret;
		},

		// private ==> used by ext full
		setOverflow: function( v )
		{
			var dom = this.dom;
			if( v == 'auto' && Ext.isMac && Ext.isGecko2 )
			{ // work around stupid FF 2.0/Mac scroll bar bug
				dom.style.overflow = 'hidden';
				(function()
				{
					dom.style.overflow = 'auto';
				}).defer(1);
			}
			else
			{
				dom.style.overflow = v;
			}
		},

		/**
		 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as a gray container with a gradient background, rounded
		 * corners and a 4-way shadow.</p> <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button}, {@link Ext.Panel} when
		 * <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}). The markup is of this form:</p>
		 * 
		 * <pre><code>
		 * Ext.Element.boxMarkup =
		 * 		 '&lt;div class=&quot;{0}-tl&quot;&gt;&lt;div class=&quot;{0}-tr&quot;&gt;&lt;div class=&quot;{0}-tc&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
		 * 		&lt;div class=&quot;{0}-ml&quot;&gt;&lt;div class=&quot;{0}-mr&quot;&gt;&lt;div class=&quot;{0}-mc&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
		 * 		&lt;div class=&quot;{0}-bl&quot;&gt;&lt;div class=&quot;{0}-br&quot;&gt;&lt;div class=&quot;{0}-bc&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; ';
		 * </code></pre>
		 * 
		 * <p>Example usage:</p>
		 * 
		 * <pre><code>
		 * // Basic box wrap
		 * Ext.get(&quot;foo&quot;).boxWrap();
		 * 
		 * // You can also add a custom class and use CSS inheritance rules to customize the box look.
		 * // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
		 * // for how to create a custom box wrap style.
		 * Ext.get(&quot;foo&quot;).boxWrap().addClass(&quot;x-box-blue&quot;);
		 * </code></pre>
		 * 
		 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to <tt>'x-box'</tt>). Note that there are a
		 *        number of CSS rules that are dependent on this name to make the overall effect work, so if you supply an alternate base class, make sure you also
		 *        supply all of the necessary rules.
		 * @return {Ext.Element} The outermost wrapping element of the created box structure.
		 */
		boxWrap: function( cls )
		{
			cls = cls || 'x-box';
			var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + String.format(Ext.Element.boxMarkup, cls) + "</div>")); // String.format('<div
			// class="{0}">'+Ext.Element.boxMarkup+'</div>',
			// cls)));
			Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
			return el;
		},

		/**
		 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
		 * 
		 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul> <li>A Number specifying the new width in this Element's
		 *        {@link #defaultUnit}s (by default, pixels).</li> <li>A String used to set the CSS width style. Animation may <b>not</b> be used. <li>A size
		 *        object in the format <code>{width: widthValue, height: heightValue}</code>.</li> </ul></div>
		 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul> <li>A Number specifying the new height in this Element's
		 *        {@link #defaultUnit}s (by default, pixels).</li> <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li> </ul></div>
		 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
		 * @return {Ext.Element} this
		 */
		setSize: function( width, height, animate )
		{
			var me = this;
			if( typeof width == 'object' )
			{ // in case of object from getSize()
				height = width.height;
				width = width.width;
			}
			width = me.adjustWidth(width);
			height = me.adjustHeight(height);
			if( !animate || !me.anim )
			{
				me.dom.style.width = me.addUnits(width);
				me.dom.style.height = me.addUnits(height);
			}
			else
			{
				me.anim(
				{
					width:
					{
						to: width
					},
					height:
					{
						to: height
					}
				}, me.preanim(arguments, 2));
			}
			return me;
		},

		/**
		 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders when needed to simulate offsetHeight
		 * when offsets aren't available. This may not work on display:none elements if a height has not been set using CSS.
		 * 
		 * @return {Number}
		 */
		getComputedHeight: function()
		{
			var me = this, h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
			if( !h )
			{
				h = parseFloat(me.getStyle('height')) || 0;
				if( !me.isBorderBox() )
				{
					h += me.getFrameWidth('tb');
				}
			}
			return h;
		},

		/**
		 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders when needed to simulate offsetWidth when
		 * offsets aren't available. This may not work on display:none elements if a width has not been set using CSS.
		 * 
		 * @return {Number}
		 */
		getComputedWidth: function()
		{
			var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
			if( !w )
			{
				w = parseFloat(this.getStyle('width')) || 0;
				if( !this.isBorderBox() )
				{
					w += this.getFrameWidth('lr');
				}
			}
			return w;
		},

		/**
		 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth() for more information about the sides.
		 * 
		 * @param {String} sides
		 * @return {Number}
		 */
		getFrameWidth: function( sides, onlyContentBox )
		{
			return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
		},

		/**
		 * Sets up event handlers to add and remove a css class when the mouse is over this element
		 * 
		 * @param {String} className
		 * @return {Ext.Element} this
		 */
		addClassOnOver: function( className )
		{
			this.hover(function()
			{
				Ext.fly(this, INTERNAL).addClass(className);
			}, function()
			{
				Ext.fly(this, INTERNAL).removeClass(className);
			});
			return this;
		},

		/**
		 * Sets up event handlers to add and remove a css class when this element has the focus
		 * 
		 * @param {String} className
		 * @return {Ext.Element} this
		 */
		addClassOnFocus: function( className )
		{
			this.on("focus", function()
			{
				Ext.fly(this, INTERNAL).addClass(className);
			}, this.dom);
			this.on("blur", function()
			{
				Ext.fly(this, INTERNAL).removeClass(className);
			}, this.dom);
			return this;
		},

		/**
		 * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
		 * 
		 * @param {String} className
		 * @return {Ext.Element} this
		 */
		addClassOnClick: function( className )
		{
			var dom = this.dom;
			this.on("mousedown", function()
			{
				Ext.fly(dom, INTERNAL).addClass(className);
				var d = Ext.getDoc(), fn = function()
				{
					Ext.fly(dom, INTERNAL).removeClass(className);
					d.removeListener("mouseup", fn);
				};
				d.on("mouseup", fn);
			});
			return this;
		},

		/**
		 * <p>Returns the dimensions of the element available to lay content out in.<p> <p>If the element (or any ancestor element) has CSS style
		 * <code>display : none</code>, the dimensions will be zero.</p> example:
		 * 
		 * <pre><code>
		 * var vpSize = Ext.getBody().getViewSize();
		 * 
		 * // all Windows created afterwards will have a default value of 90% height and 95% width
		 * Ext.Window.override(
		 * {
		 * 	width: vpSize.width * 0.9,
		 * 	height: vpSize.height * 0.95
		 * });
		 * // To handle window resizing you would have to hook onto onWindowResize.
		 * </code></pre>
		 * 
		 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars. To obtain the size including scrollbars, use getStyleSize Sizing of
		 * the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
		 */

		getViewSize: function()
		{
			var doc = document, d = this.dom, isDoc = (d == doc || d == doc.body);

			// If the body, use Ext.lib.Dom
			if( isDoc )
			{
				var extdom = Ext.lib.Dom;
				var ret =
				{
					width: extdom.getViewWidth(),
					height: extdom.getViewHeight()
				};

				return ret;

				// Else use clientHeight/clientWidth
			}
			else
			{
				var ret =
				{
					width: d.clientWidth,
					height: d.clientHeight
				};

				return ret;
			}
		},

		/**
		 * <p>Returns the dimensions of the element available to lay content out in.<p> getStyleSize utilizes prefers style sizing if present, otherwise it
		 * chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth. To obtain the size excluding scrollbars, use getViewSize Sizing of the
		 * document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
		 */

		getStyleSize: function()
		{
			var me = this, w, h, doc = document, d = this.dom, isDoc = (d == doc || d == doc.body), s = d.style;

			// If the body, use Ext.lib.Dom
			if( isDoc )
			{
				var extdom = Ext.lib.Dom;
				var ret =
				{
					width: extdom.getViewWidth(),
					height: extdom.getViewHeight()
				};

				return ret;
			}
			// Use Styles if they are set
			if( s.width && s.width != 'auto' )
			{
				w = parseFloat(s.width);
				if( me.isBorderBox() )
				{
					w -= me.getFrameWidth('lr');
				}
			}
			// Use Styles if they are set
			if( s.height && s.height != 'auto' )
			{
				h = parseFloat(s.height);
				if( me.isBorderBox() )
				{
					h -= me.getFrameWidth('tb');
				}
			}
			// Use getWidth/getHeight if style not set.
			var ret =
			{
				width: w || me.getWidth(true),
				height: h || me.getHeight(true)
			};

			return ret;
		},

		/**
		 * Returns the size of the element.
		 * 
		 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
		 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
		 */
		getSize: function( contentSize )
		{
			var ret =
			{
				width: this.getWidth(contentSize),
				height: this.getHeight(contentSize)
			};

			return ret;
		},

		/**
		 * Forces the browser to repaint this element
		 * 
		 * @return {Ext.Element} this
		 */
		repaint: function()
		{
			var dom = this.dom;
			this.addClass("x-repaint");
			setTimeout(function()
			{
				Ext.fly(dom).removeClass("x-repaint");
			}, 1);
			return this;
		},

		/**
		 * Disables text selection for this element (normalized across browsers)
		 * 
		 * @return {Ext.Element} this
		 */
		unselectable: function()
		{
			this.dom.unselectable = "on";
			return this.swallowEvent("selectstart", true).addClass("x-unselectable");
		},

		/**
		 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed, then it returns the
		 * calculated width of the sides (see getPadding)
		 * 
		 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
		 * @return {Object/Number}
		 */
		getMargins: function( side )
		{
			var me = this, key, hash =
			{
				t: "top",
				l: "left",
				r: "right",
				b: "bottom"
			}, o = {};

			if( !side )
			{
				for( key in me.margins )
				{
					o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
				}
				return o;
			}
			else
			{
				return me.addStyles.call(me, side, me.margins);
			}
		}
	};
}());
